﻿// Include the namespace required to use Unity UI
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UI;

public class MyPlayerController : MonoBehaviour {
	
	// Create public variables for player speed, and for the Text UI game objects
	public float speed = 10;
	public Text countText;

	// Create private references to the rigidbody component on the player, and the count of pick up objects picked up so far
	private Rigidbody rb;
	private int count;
	private GrpcGameClient grpc;
	private Transform tr;
	private FloatingJoystick floatingJoystick;
	private Transform forceCanvas;

	// 限制更新次数，仅在时长超过 0.5s, 或位置超过 0.1m 时才发送更新
	private float updateDeltaTime;  // 上次更新时长
	private float updateX, updateZ;  // 上次更新位置

	// At the start of the game..
	void Start ()
	{
		Camera.main.GetComponent<CameraController>().SetPlayer(gameObject);

		// Assign the Rigidbody component to our private rb variable
		rb = GetComponent<Rigidbody>();
		Assert.IsNotNull(rb);
		var go = GameObject.Find("Count Text");
		Assert.IsNotNull(go);
		countText = go.GetComponent<Text>();
		Assert.IsNotNull(countText);

		// Set the count to zero 
		count = 0;

		// Run the SetCountText function to update the UI (see below)
		SetCountText ();

		grpc = GrpcGameClient.Instance;
		tr = transform;
		go = GameObject.Find("Floating Joystick");
		Assert.IsNotNull(go);
		floatingJoystick = go.GetComponent<FloatingJoystick>();
		Assert.IsNotNull(floatingJoystick);
		go = GameObject.Find("Force Canvas");
		Assert.IsNotNull(go);
		forceCanvas = go.GetComponent<Transform>();
	}

	// Each physics step..
	async void FixedUpdate ()
	{
		// Set some local float variables equal to the value of our Horizontal and Vertical Inputs
		float moveHorizontal = Input.GetAxis("Horizontal") + floatingJoystick.Horizontal;
		float moveVertical = Input.GetAxis("Vertical") + floatingJoystick.Vertical;

		// Create a Vector3 variable, and assign X and Z to feature our horizontal and vertical float variables above
		Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);

		// 显示力的方向和大小
		forceCanvas.position = tr.position + Vector3.down * 0.49f;
		float r = Mathf.Atan2(moveHorizontal, moveVertical);
		forceCanvas.eulerAngles = new Vector3(90, r * Mathf.Rad2Deg, 0);
		forceCanvas.localScale = new Vector3(1, movement.magnitude, 1);

		// Add a physical force to our Player rigidbody using our 'movement' Vector3 above, 
		// multiplying it by 'speed' - our public player speed that appears in the inspector
		rb.AddForce (movement * speed);

		await TryToUpdateAsync();
	}

	// Create a standalone function that can update the 'countText' UI and check if the required amount to win has been achieved
	void SetCountText()
	{
		// Update the text field of our 'countText' variable
		countText.text = "Count: " + count.ToString ();
	}

	public void IncCount()
    {
		count++;
		SetCountText();
    }

	private async Task TryToUpdateAsync()
    {
		if (!NeedUpdate())
		{
			updateDeltaTime += Time.deltaTime;
			return;
		}
		updateDeltaTime = 0;
		updateX = tr.position.x;
		updateZ = tr.position.z;

		await grpc.UpdateAsync(tr);
	}

	// 是否需要向服务器发送更新
	private bool NeedUpdate()
    {
		if (updateDeltaTime > 0.5)
			return true;
		return Mathf.Abs(updateX - tr.position.x)
			+ Mathf.Abs(updateZ - tr.position.z) > 0.1;
    }
}